/** @brief Fichier déclarant une classe de parsing de XML.
 *
 * Ce fichier contient la déclaration d'une classe permettant parser un fichier XML.
 *
 * @file Parser.hpp
 * @author Johan "Solo" Luisier
 * @date 2009/06/04
 */
#ifndef PARSER_H
#define PARSER_H

#include "ElementDOM.hpp"

#include "Utils/debugstreams.hpp"
#include "Utils/StatusCode.hpp"

#include <map>
#include <stack>

using namespace std;

/** @brief Classe implémentant le parsing de fichier XML.
 *
 * Cette classe est un parseur XML basé sur l'implémentation DOM : on
 * lit le fichier, que l'on convertit en un "arbre", et on crée une
 * map qui lie chaque tag à un ElementDOM. Peut sauter automatiquement les
 * commentaires et headers (version XML). Permet de lire n'importe
 * quel fichier XML (avec ou sans attribut dans les champs de tag).
 *
 * @see ElementDOM
 * @author Johan "Solo" Luisier
 * @date 2009/06/04
 */
class Parser
{
public:
    /** @brief Constructeur par défaut.
     *
     * Ce constructeur offre la possibilité d'initialiser directement le nom du
     * fichier à lire.
     *
     * @param[in] nom_fichier nom du fichier à lire
     */
    Parser(const string& nom_fichier = "" );
    /** @brief Destructeur, ne fait rien de spécial.
     *
     * Le destructeur peut afficher la structure de l'arbre si la valeur
     * de Parser::elementaire est fixée à @e INFO dans le fichier
     * CONFIG.
     */
    virtual ~Parser();
    /** @brief Mutateur pour NomFichier.
     *
     * Cette méthode permet de modifier la valeur de NomFichier.allocator_type
     *
     * @param[in] nom_fichier nouvelle valeur de NomFichier.
     */
    void setNomFichier(const string& nom_fichier);
    /** @brief Méthode chargeant le contenu du fichier.
     *
     * Charge le contenu du fichier dans la structure de donnée interne
     * (le fichier est mis dans une string, sans retours à la ligne).
     *
     * @return StatusCode @c true si tout s'est bien déroulé, valeur()
     * donne le nombre de lignes. Dans le cas où un problème apparaît,
     * message() est aussi rempli.
     */
    StatusCode chargerFichier();
    /** @brief Méthode interprétant le contenu du fichier XML.
     *
     * Méthode qui interprète le XML. On procède de manière récursive : si
     * la balise s'ouvre puis se ferme, on va écrire le champ de donnée,
     * ainsi que le "numéro" du champ, qui permet ensuite de reproduire la
     * structure du fichier. Si la balise ne contient pas de données mais
     * d'autres balises, elle est inscrite dans la structure, mais aucune
     * donnée ne lui est associée.\n
     *
     * Les balises sont stockées dans un stack, afin de s'assurer que
     * chaque tag ouvert est bien refermé en respectant la syntaxe XML\n
     *
     * La lecture s'arrête lorsque le stack est vide : c'est à dire
     * lorsque toutes les balises ouvrantes ont été fermées.
     *
     * @return StatusCode @c true si la lecture de l'ElementDOM s'est
     * effectuée sans problème, @c false dans le cas contraire. Un message
     * peut être associé.
     */
    StatusCode lireDocument();
    /** @brief Mutateur pour les niveaux de sorties.
     *
     * Permet de fixer les deux niveaux de sorties (elementaire et
     * fonction).
     *
     * @param[in] lequel 0 pour Parser::elementaire, 1 pour Parser::fonction
     * @param[in] lvl niveau d'affichage
     */
    static void setLevel(const DebugLevelType& lvl, const unsigned& lequel);
    /** @brief Accesseur pour Informations.
     *
     * Permet de récupérer les informations lues, sous forme de
     * @c map<string,\ string>.
     *
     * @param[out] infosLues map<string,\ string>, qui est remplie par la
     * méthode.
     *
     * @return StatusCode, @c false si le conteneur est vide.
     */
    StatusCode getInformations(map< string, string >& infosLues) const;
    /** @brief Accesseur pour Arbre.
     *
     * Permet de récupérer l'ordre des balise, afin de recomposer la
     * structure du fichier.
     *
     * @param[out] structLue conteneur où est récupéré l'ordre.
     *
     * @return StatusCode, false si le conteneur est vide.
     */
    StatusCode getStructureFichier(vector< const ElementDOM* >& structLue) const;
    /** @brief Accesseur pour la première instance de ElementDOM du document.
     *
     * Permet de récupérer le premier enfant du document.
     *
     * @retval ptr la première instance de ElementDOM du document, @retval 0 si
     * le document ne contient pas de telle instance.
     */
    const ElementDOM* premierEnfant(const ElementDOM* elDom = 0) const;
    /** @brief Accesseur pour le "frère" d'une instance d'ElementDOM donnée.
     *
     * Permet de récupérer le "frère" suivant d'un ElementDOM donné, le frère
     * est définit comme l'instance du même niveau que @c elDom qui se trouve
     * juste après @c elDom dans @c Arbre.
     *
     * @param elDom pointeur sur l'élément dont on veut le frère.
     *
     * @retval ptr le frère de @c elDom, @retval 0 si @c elDom n'a pas de frère.
     */
    const ElementDOM* prochainFrere(const ElementDOM*& elDom) const;
protected:
    /**
     * Nom du fichier qui sera lu.
     */
    string NomFichier;
    /**
     * Contenu du fichier, sans retour à la ligne.
     */
    string Donnees;
    /**
     * Nombre de caractères présent dans donnees.
     */
    unsigned Taille;
    /**
     * Contient l'association balise - ElementDOM. La balise utilisée
     * ici peut être modifiée (ajout de '+') si une balise avec le même
     * nom est déjà présente.
     */
    map< string, ElementDOM > Informations;
    /**
     * Sert à la vérification de la syntaxe XML.
     */
    stack< string > Pile;
    /**
     * Sert à l'identification du parent de chaque ElementDOM. C'est le
     * numero de l'élément qui contient l'élément précédent.
     */
    stack< const ElementDOM* > Parents;
    /**
     * Numérote les ElementDOM trouvés : permet ensuite de reconstruire
     * la structure du fichier à partir de Informations (dans un map,
     * l'ordre n'est pas préservé).
     */
    unsigned NumeroElement;
    /**
     * Stocke les pointeurs sur les ElementDOM, afin de pouvoir simplement
     * reconstruire le fichier dans sa structure originale.
     */
    vector< ElementDOM* > Arbre;
    /**
     * Sert à vérifier que les données ont bien été lues.
     */
    bool DonneesLues;
private:
    static DebugLevelType Elementaire, /**< Niveau de sortie des
                    fonctions basiques */
    Fonction; /**< Niveau de sortie des fonctions de haut niveau. */
    /**
     * Position de lecture du fichier .xml.
     */
    int Index;
    /** @brief Lit le document et s'arrête à la première balise ouvrante trouvée.
     *
     * La méthode cherche le premier caractère @c '<' troubé depuis la dernière
     * position de lecture.
     *
     * @return StatusCode, @c true si une balise ouvrante a bien été trouvée,
     * auquel cas l'index est donné par valeur(). Si on a pas trouvé de balise
     * ouvrante, on renvoie @c false, le code d'erreur 10 et un message.
     */
    StatusCode trouverOuvrante ();
    /** @brief Lit le document et s'arrête à la fin de la balise courante.
     *
     * La méthode cherche le premier caractère @c '>' depuis la dernière
     * position de lecture.
     *
     * @return StatusCode, \e true si une balise fermante a bien été
     * trouvée, auquel cas l'index est donné par valeur(). Si on a pas
     * trouvé de balise fermante, on renvoie \e false, le code d'erreur 11
     * et un message.
     */
    StatusCode trouverFermante ();
    /** @brief Permet de s'affranchir des ' ' entre deux balises, deux informations, ...
     *
     * La méthode avance l'index de lecture jusqu'au moment où il n'y a plus
     * d'espaces.
     *
     * FUNC  permet d'afficher les messages de début et de fin
     */
    void sauterBlanc();
    /** @brief Méthode supprimant les espaces inutiles dans une string.
     *
     * Fait disparaître les ' ' à la fin d'une string lors de la récupération
     * depuis un fichier .xml
     */
    void wipeBlank(string& avecEspaces);
    /** @brief Lit le contenu d'e la'une balise
     *
     * FUNC  permet d'afficher les messages de début et de fin
     *
     */
    StatusCode litBalise();
    /** @brief Permet de sauter un commentaire
     *
     * FUNC  permet d'afficher les messages de début et de fin
     *
     */
    StatusCode skipComment();
    /** @brief Méthode vérifiant si la prochaine balise est bien du type
     * attendu.
     *
     * Gestion de la bonne structure du fichier .xml
     * Cherche le contenu de la balise suivante et le compare à la string b
     *
     */
    StatusCode verifierContenuFutur(const string& future);
    /** @brief Méthode construisant une instance de ElementDom depuis le
     * contenu d'une balise.
     *
     * Lit la balise et construit un ElementDOM avec les éventuels attributs
     * présents. Est capable de lire une balise du type @c \<contenu/\>.
     */
    ElementDOM isolerAttributs(string& balise);

};

#endif // PARSER_H
